package com.link.gateway.core.filter.visitstatistics.util;

import com.alibaba.fastjson.JSON;
import com.link.gateway.base.interfacelog.model.InterfaceLog;
import com.link.gateway.base.user.model.User;
import com.link.gateway.core.comproperty.InterfaceLogConstants;
import com.link.gateway.core.kafka.KafkaTemplateFacotry;
import com.link.gateway.core.redis.redisclient.LinkRedisClient;
import com.link.gateway.core.utils.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.server.reactive.ServerHttpRequest;

/**
 * 执行超频拒绝后的操作
 *
 * @author LiYang
 * @date 20190827
 */
public class ProcessRestrictionVisit implements Runnable {

    private static final Logger logger = LogManager.getLogger(ProcessRestrictionVisit.class);

    private ServerHttpRequest request;

    private static final String LOCK_KEY_PREFIX = "alert_lock_";

    private static final String ALERT_KEY_PREFIX = "alert_key_";

    private static final String LOCK_KEY_VALUE = "locked";

    public ProcessRestrictionVisit() {

    }

    public ProcessRestrictionVisit(ServerHttpRequest request) {
        this.request = request;
    }

    @Override
    public void run() {
        String url = VisitUtil.getUrl(request);
        String ip = IpUtil.getIpAddress(request);
        //将超频记录写入记录表
        try {
            InterfaceLog interfaceLog = new InterfaceLog();
            User user = TokenUtils.getTokenUser();
            if (null != user) {
                interfaceLog.setUserName(user.getUsername());
                interfaceLog.setUserFirstName(user.getFirstName());
                interfaceLog.setCorpId(user.getLoginCorpId());
            }
            interfaceLog.setVisitType(InterfaceLogConstants.RESTRICTION);
            interfaceLog.setUrl(url);
            interfaceLog.setIp(ip);
            interfaceLog.setStartTime(System.currentTimeMillis());

            String interfaceLogStr = JSON.toJSONString(interfaceLog);
        } catch (Exception e) {
            logger.error(e);
        }
        //处理短信和邮件发送
        LinkRedisClient jedis = null;
        String lockKey = "{" + LOCK_KEY_PREFIX + url + "}";
        try {
            jedis = RedisUtil.getJedis();
            //处理短信和邮件发送入口方法
            processSendAlert(jedis, url, lockKey);
        } catch (Exception e) {
            logger.error(e);
        } finally {
            RedisUtil.returnResource(jedis);
        }
    }

    /**
     * 处理报警邮件和短信提醒
     *
     * @author LiYang
     * @date 20190827
     */
    private void processSendAlert(LinkRedisClient jedis, String url, String lockKey) {
        VisitConfigSingleton visitConfig = VisitConfigSingleton.getInstance();
        String limitTime = "";
        String limitNum = "";
        if ("/base/user/login".equalsIgnoreCase(url)) {
            limitTime = visitConfig.getLoginLimitTime();
            limitNum = visitConfig.getLoginLimitNum();
        } else if (null != visitConfig.getSpecialUrl().get(url)) {
            limitTime = visitConfig.getSpecialLimitTime();
            limitNum = visitConfig.getSpecialLimitNum();
        } else {
            limitTime = visitConfig.getLimitTime();
            limitNum = visitConfig.getLimitVisitNum();
        }
        try {
            boolean isSend = isSend(jedis, url, lockKey);
            boolean isSendMsg = "Y".equalsIgnoreCase(visitConfig.getMsgAlert()) && isSend;
            //如果配置了短信通知，执行下面if
            if (isSendMsg) {
                String msgParam = jedis.hget(VisitStatisticsConstants.API_STATISTICS_CONFIG_KEY, VisitStatisticsConstants.API_CONFIG_MSG_PARAMS);
                msgParam = msgParam.replace("url_msg", url);
                msgParam = msgParam.replace("hint_msg", "访问频率（"
                        + limitTime + "秒内访问超过" + limitNum + "次）");
                KafkaTemplateFacotry.send(KafkaTemplateFacotry.SEND_RESTRICTION_MAIL_TOPIC, msgParam.toString());
            }
            boolean isSendEmail = "Y".equalsIgnoreCase(visitConfig.getEmailAlert()) && isSend;
            //如果配置了邮件通知，执行下面if
            if (isSendEmail) {
                String receiveAddr = jedis.hget(VisitStatisticsConstants.API_STATISTICS_CONFIG_KEY,
                        VisitStatisticsConstants.API_CONFIG_EMAIL_ADDRS);
                //获取收件地址
                String[] addressee = receiveAddr.split(",");
                //定义标题
                String title = "接口访问提示";
                //定义内容
                String content = "尊敬的用户您好,系统接口" + url + "在" + limitTime + "秒内访问超过" + limitNum + "次，请注意检查！";
                EmailUtil.sendEmail(title, content, addressee);
            }
        } catch (Exception e) {
            logger.error(e);
        }
    }

    /**
     * 判断是否需要发送预警短信和邮件
     *
     * @author LiYang
     * @date 20190826
     * @param jedis
     * @param url
     * @param lockKey
     * @return
     */
    private boolean isSend(LinkRedisClient jedis, String url, String lockKey) {
        boolean alertFlag = false;
        try {
            VisitConfigSingleton visitConfig = VisitConfigSingleton.getInstance();
            String alertTimeInterval = visitConfig.getAlertTimeInterval();
            String loginTimeInterval = visitConfig.getLoginAlertInterval();
            String normalKey = "{" + ALERT_KEY_PREFIX + "normal" + "}";
            String loginKey = "{" + ALERT_KEY_PREFIX + "login" + "}";
            long currentTime = System.currentTimeMillis();
            int tryCount = 0;
            //如果当前接口为用户登录接口，单独处理
            if ("/base/user/login".equalsIgnoreCase(url)) {
                while (true) {
                    //5秒后锁自动过期
                    boolean flag = RedisTool.tryGetDistributedLock(jedis,
                            lockKey, LOCK_KEY_VALUE, 5000);
                    if (flag) {
                        break;
                    } else {
                        if (tryCount > 10) {
                            return alertFlag;
                        }
                        //每隔50毫秒尝试获取锁
                        Thread.sleep(50);
                        tryCount ++;
                    }
                }
                String lastAlertTime = jedis.get(loginKey);
                //如果没有取出上次报警时间，返回报警状态未true，并设置当前时间为上次报警时间
                if (StringUtils.isBlank(lastAlertTime) || "null".equalsIgnoreCase(lastAlertTime)) {
                    jedis.set(loginKey, String.valueOf(currentTime));
                    alertFlag = true;
                } else {
                    //如果此刻与上次发送信息的时间差大于设置的时间间隔，就讲标记设为true
                    alertFlag = currentTime - Long.parseLong(lastAlertTime) > Long.parseLong(loginTimeInterval) * 60 * 1000;
                    if (alertFlag) {
                        jedis.set(loginKey, String.valueOf(currentTime));
                        alertFlag = true;
                    }
                }
            } else {
                while (true) {
                    //5秒后锁自动过期
                    boolean flag = RedisTool.tryGetDistributedLock(jedis,
                            lockKey, LOCK_KEY_VALUE, 5000);
                    if (flag) {
                        break;
                    } else {
                        if (tryCount > 10) {
                            return alertFlag;
                        }
                        //每隔50毫秒尝试获取锁
                        Thread.sleep(50);
                        tryCount ++;
                    }
                }
                String lastAlertTime = jedis.get(normalKey);
                if (StringUtils.isBlank(lastAlertTime) || "null".equalsIgnoreCase(lastAlertTime)) {
                    jedis.set(normalKey, String.valueOf(currentTime));
                    alertFlag = true;
                } else {
                    //如果此刻与上次发送信息的时间差大于设置的时间间隔，就讲标记设为true
                    alertFlag = currentTime - Long.parseLong(lastAlertTime) > Long.parseLong(alertTimeInterval) * 60 * 1000;
                    if (alertFlag) {
                        jedis.set(normalKey, String.valueOf(currentTime));
                        alertFlag = true;
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e);
        } finally {
            RedisTool.releaseDistributedLock(jedis, lockKey, LOCK_KEY_VALUE);
        }
        return alertFlag;
    }
}
